package com.smartnsoft.fractory;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;

import org.fractals.Mandelbrot;
import org.jfract.abstractBeans.AbstractFractalBean;
import org.jfract.util.CompletionListener;
import org.jfract.util.Location;
import org.jfract.util.PictureProcessor;

import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.os.Environment;
import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.Toast;

import com.custom.windows.AskSaveProjectPopup;
import com.custom.windows.QualityPopup;
import com.custom.windows.SaveProjectPopup;
import com.smartnsoft.droid4me.app.AppPublics;
import com.smartnsoft.droid4me.app.AppPublics.BroadcastListener;
import com.smartnsoft.droid4me.app.SmartActivity;
import com.smartnsoft.droid4me.log.Logger;
import com.smartnsoft.droid4me.log.LoggerFactory;
import com.smartnsoft.fractory.EditorPanel.CanvasListener;
import com.smartnsoft.fractory.EditorPanel.LocationListener;
import com.smartnsoft.fractory.bo.Project;
import com.smartnsoft.fractory.ws.FractoryServices;

/**
 * Manage the display of the fractal.
 * 
 * @author Willy Noel
 * @since 2011.05.25
 */
public final class EditorActivity
    extends SmartActivity<Void>
    implements AppPublics.BroadcastListenerProvider, CompletionListener,CanvasListener,
    LocationListener, View.OnClickListener
{

  protected AbstractFractalBean fractal;

  protected final static Logger log = LoggerFactory.getInstance(EditorActivity.class);

  public static final String PROJECT = "project";

  public static String FRACTAL = "fractale";

  private PictureProcessor processor = null;

  private int width = 40;

  private int height = 41;

  private int maxIteration = 20;

  public static int MAX_ITERATION_LOW = 20;

  private EditorPanel editorPanel;

  private RelativeLayout menuBarLeft;

  private RelativeLayout menuBarRight;

  private ImageButton setQualityButton;

  private ImageButton saveProjectButton;

  private boolean hideMenuBars = false;

  private QualityPopup qualityWindows;

  private SaveProjectPopup saveProjectWindow;

  private String directoryName;

  private File contentsDirectoryProjectStorage;

  private File contentsDirectoryImageStorage;

  private ImageButton renderButton;

  private AskSaveProjectPopup askSaveProjectWindow;

  private Project project;

  private boolean saveBeforeQuit = true;

  private ImageButton setAsWallpaperButton;

  public void onRetrieveDisplayObjects()
  {
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    Display display = getWindowManager().getDefaultDisplay();
    width = display.getWidth();
    height = display.getHeight();

    setContentView(R.layout.editor_screen);
    editorPanel = (EditorPanel) findViewById(R.id.editorPanel);
    menuBarLeft = (RelativeLayout) findViewById(R.id.menuBarLeft);
    menuBarRight = (RelativeLayout) findViewById(R.id.menuBarRight);
    setQualityButton = (ImageButton) findViewById(R.id.qualityButton);
    saveProjectButton = (ImageButton) findViewById(R.id.saveProjectButton);
    renderButton = (ImageButton) findViewById(R.id.renderButton);
    setAsWallpaperButton = (ImageButton) findViewById(R.id.setAsWallpaperButton);

    qualityWindows = new QualityPopup(editorPanel);
    qualityWindows.setRootView(R.layout.custom_window_quality);
    saveProjectWindow = new SaveProjectPopup(editorPanel);
    saveProjectWindow.setRootView(R.layout.custom_window_save_project);
    askSaveProjectWindow = new AskSaveProjectPopup(editorPanel);
    askSaveProjectWindow.setRootView(R.layout.custom_window_ask_save_project);

    directoryName = getPackageManager().getApplicationLabel(getApplicationInfo()).toString();

    editorPanel.setLocationListener(this);
    editorPanel.setCanvasListener(this);
    renderButton.setOnClickListener(this);
    setQualityButton.setOnClickListener(this);
    saveProjectButton.setOnClickListener(this);
    setAsWallpaperButton.setOnClickListener(this);
    qualityWindows.qualityCancelButton.setOnClickListener(this);
    qualityWindows.qualityValidateButton.setOnClickListener(this);
    saveProjectWindow.saveProjectCancelButton.setOnClickListener(this);
    saveProjectWindow.saveProjectValidateButton.setOnClickListener(this);
    askSaveProjectWindow.askSaveProjectCancelButton.setOnClickListener(this);
    askSaveProjectWindow.askSaveProjectSaveButton.setOnClickListener(this);
    askSaveProjectWindow.askSaveProjectDontSaveButton.setOnClickListener(this);
  }

  public void onRetrieveBusinessObjects()
      throws BusinessObjectUnavailableException
  {
    // setIntent(newIntent())
    // if (getIntent().getAction() != null)
    // {
    // if (getIntent().getAction().equals(EditorActivity.FRACTAL) == true)
    // {
    // TODO : impement with the intent instead of this

    fractal = (AbstractFractalBean) getIntent().getSerializableExtra(EditorActivity.FRACTAL);
    if (fractal != null)
    {
      fractal.setIter(MAX_ITERATION_LOW);
      fractal.setColor(fractal.COLORWHITE);
      fractal.setInternColor(fractal.COLORBLACK_TO_RED);
    }

    // }
    // else if (getIntent().getAction().equals(EditorActivity.PROJECT) == true)
    // {
    if (fractal == null)
    {
      project = (Project) getIntent().getSerializableExtra(EditorActivity.PROJECT);
      fractal = new Mandelbrot(this, project.name, project.expression, project.type, new Location(project.x, project.y, project.zoom));
      fractal.setIter(MAX_ITERATION_LOW);
      fractal.setColor(fractal.COLORWHITE);
      fractal.setInternColor(fractal.COLORBLACK_TO_RED);

      // no need to save project during the first draw
      saveBeforeQuit = false;
    }
    // }

  }

  public void onFulfillDisplayObjects()
  {
    processor = new PictureProcessor(fractal, width, height, this, null);
    editorPanel.setProcessor(processor);
    editorPanel.setFractal(fractal);
    editorPanel.setWidth(width);
    editorPanel.setHeight(height);
    //editorPanel.setImage();
    // we indicate the last location to the editorPanel screen to draw the fractal correctly
    // if the fractal come from a saved project
    if (project != null)
    {
      editorPanel.setFractalPosition((new PointF(project.x, project.y)));
      editorPanel.setZoom(project.zoom);
     // editorPanel.setBackgroundDrawable((BitmapFactory.decodeFile(project.pathBitmap));
    }
    processor.start();
  }

  public void onSynchronizeDisplayObjects()
  {

  }

  public BroadcastListener getBroadcastListener()
  {

    return new AppPublics.BroadcastListener()
    {

      public IntentFilter getIntentFilter()
      {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(EditorPanel.ON_TAP_ACTION);
        intentFilter.addAction(EditorActivity.PROJECT);
        intentFilter.addAction(EditorActivity.FRACTAL);
        return intentFilter;
      }

      public void onReceive(Intent intent)
      {
        // if (intent.getAction().equals(EditorActivity.PROJECT) == true)
        // {
        // project = (Project) getIntent().getSerializableExtra(EditorActivity.PROJECT);
        // }

        if (intent.getAction().equals(EditorPanel.ON_TAP_ACTION) == true)
        {
          log.debug("HideMenu = '" + hideMenuBars + "'");
          hideMenuBars = !hideMenuBars;
          // TODO: understand why it doesn't work when we take the full width of the menu
          final float menuWidth = menuBarLeft.getMeasuredWidth() - 1;
          // final float accountViewOriginalHeight = footerBlock.getMeasuredHeight();
          final Animation animation = new Animation()
          {
            @Override
            protected void applyTransformation(float interpolatedTime,
                Transformation transformation)
            {
              ((MarginLayoutParams) menuBarLeft.getLayoutParams()).leftMargin = (int) ((hideMenuBars == true ? 0
                  : -menuWidth) - (hideMenuBars == true ? 1 : -1) * (interpolatedTime * menuWidth));
              menuBarLeft.requestLayout();
              ((MarginLayoutParams) menuBarRight.getLayoutParams()).rightMargin = (int) ((hideMenuBars == true ? 0
                  : -menuWidth) - (hideMenuBars == true ? 1 : -1) * (interpolatedTime * menuWidth));
              menuBarRight.requestLayout();
            }
          };
          animation.setDuration(600);
          (hideMenuBars == true ? menuBarLeft : menuBarRight).startAnimation(animation);
        }
      }
    };

  }

  public void completionEventPerformed(int step, int max)
  {
	  
  }

  public void newImageComputed(int[] resultIter, int xOffset, int yOffset,
      int width, int height, boolean lastPartialImage)
  {
    if (lastPartialImage)
    {
      editorPanel.setClearImgs(true);
    }
    editorPanel.setWidthNewImComputed(width);
    editorPanel.setHeightNewImComputed(height);
    int[] appendColorAndInternal = fractal.getAppendColorAndInternal();
    // int[] resultIter = processor.getResultColor();
    int[] resultColor = new int[resultIter.length];
    int index = 0;
    for (int result : resultIter)
    {
      try
      {
        resultColor[index++] = appendColorAndInternal[result];
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }
    }
    // TODO : out of memory.

    //Bitmap img = Bitmap.createBitmap(resultColor, editorPanel.getWidthNewImComputed(), editorPanel.getHeightNewImComputed(), Bitmap.Config.ARGB_8888);
    Bitmap img = Bitmap.createBitmap(resultColor, width, height, Bitmap.Config.ARGB_8888);
    //@Willy the problem seems to comes from here we add some pics in the List of img and
    //at a step there is not enough memory to add another image.
    //Could you use a kind of your kit soft to see how memory remains ? 
    //could you tune a memory increase in the settings ?
    
    editorPanel.getListImgs().add(new ImageWithOffset(img, xOffset, yOffset));
 
    if (lastPartialImage)
    {
      editorPanel.setRefresh(true);
      //@Willy how do you finnally display the picture on the screen
      //I suppose that you were blocked by memory issues
      //a workaround (just to continue your dev) could be to reduce the size of pic to 400/400 for example.
      editorPanel.postInvalidate();
     
    }
//   if(lastPartialImage == false)
//   {
//     img.recycle();
//     img = null;
//   }
// 
    processor.notifyThread();
    editorPanel.setRefresh(false);
    // processor.pause();
  }

  public void rendering()
  {

  }

  public void saveProject(AbstractFractalBean fractal, String name)
      throws IOException
  {
    // Draw the fractal on a virgin bitmap
    Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(image);
    editorPanel.draw(canvas);

    contentsDirectoryProjectStorage = new File(getFilesDir() + "/" + directoryName);
    contentsDirectoryImageStorage = new File(getCachePath());

    // test
    if (!contentsDirectoryProjectStorage.exists())
    {
      contentsDirectoryProjectStorage.mkdir();
    }
    if (!contentsDirectoryImageStorage.exists())
    {
      contentsDirectoryImageStorage.mkdir();
    }

    // Store the bitmap on the intern memory
    String pathBitmap = contentsDirectoryImageStorage.getPath() + "/" + name + "_bitmap.jpg";
    log.debug("PATH ='" + pathBitmap + "'");
    FileOutputStream fileOutputStream = null;
    fileOutputStream = new FileOutputStream(pathBitmap);
    BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);
    image.compress(CompressFormat.JPEG, 100, bos);
    bos.flush();
    bos.close();

    // Create a new Project to store
    final Calendar c = Calendar.getInstance();
    int mYear = c.get(Calendar.YEAR);
    int mMonth = c.get(Calendar.MONTH);
    int mDay = c.get(Calendar.DAY_OF_MONTH);
    Project newProject = new Project(name, fractal.getType(), fractal.getExpression(), fractal.getLocation().x, fractal.getLocation().y, fractal.getLocation().zoom, new Date(mYear, mMonth, mDay), pathBitmap);

    // if this is a new project we test if there are a project with a similar name
    if (project == null)
    {
      if (FractoryServices.getInstance().testNameProject(newProject))
      {
        FractoryServices.getInstance().toggleProjects(newProject);
        (this).runOnUiThread(new Runnable()
        {
          public void run()
          {
            Toast.makeText(getApplicationContext(), getApplicationContext().getString(R.string.Toast_save_project), Toast.LENGTH_SHORT).show();
          }
        });
      }
      else
      {
        (this).runOnUiThread(new Runnable()
        {
          public void run()
          {
            Toast.makeText(getApplicationContext(), getApplicationContext().getString(R.string.Toast_simalar_name), Toast.LENGTH_LONG).show();
          }
        });
      }
    }
    else
    {
      FractoryServices.getInstance().toggleProjects(newProject);
      (this).runOnUiThread(new Runnable()
      {
        public void run()
        {
          Toast.makeText(getApplicationContext(), getApplicationContext().getString(R.string.Toast_save_project), Toast.LENGTH_SHORT).show();
        }
      });
    }

  }

  public final String getCachePath()
  {
    return Environment.getExternalStorageDirectory() + Constants.DATA_FILE_DIRECTORY;
  }

  public void refreshLocation(Location location)
  {
    saveBeforeQuit = true;
    fractal.setLocation(location);
    fractal.setIter(MAX_ITERATION_LOW);
    processor = new PictureProcessor(fractal, width, height, this, null);
    processor.start();
  }

  public Bitmap getImageFromCanvas()
  {
    // Draw the fractal on a virgin bitmap
    Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(image);
    editorPanel.draw(canvas);
    
    editorPanel.setImage(image);
    return image;
  }

  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event)
  {
    if (event.getAction() == KeyEvent.ACTION_DOWN)
    {
      if (saveBeforeQuit == true)
      {
        askSaveProjectWindow.show();
        
      }
      else
      {
        this.finish();
      }
      return true;
    }

    return super.onKeyDown(keyCode, event);
  }

  public void onClick(View view)
  {
    /*************************************** MENU BAR BUTTON ACTION*********************/
    if (view == setQualityButton)
    {
      qualityWindows.show();
    }
    else if (view == qualityWindows.qualityCancelButton)
    {
      qualityWindows.dismiss();
    }
    else if (view == saveProjectButton)
    {
      if (project != null)
      {
        try
        {
          saveProject(fractal, project.name);
          saveBeforeQuit = false;
        }
        catch (IOException e)
        {
          e.printStackTrace();
        }
      }
      else
      {
        saveProjectWindow.show();
      }
    }
    else if (view == renderButton)
    {
      processor = new PictureProcessor(fractal, width, height, this, null);
      processor.start();
    }
    else if(view == setAsWallpaperButton)
    {
      Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
      Canvas canvas = new Canvas(image);
      editorPanel.draw(canvas);
      new FractalFeed(this, image).setAsWallpaper(getHandler());
    }

    /*************************************** WINDOWS BUTTON ACTION *********************/
 
    else if (view == qualityWindows.qualityValidateButton)
    {
      qualityWindows.dismiss();
      maxIteration = qualityWindows.getMaxIteration();
      fractal.setIter(maxIteration);
    }
    else if (view == saveProjectWindow.saveProjectCancelButton)
    {
      saveProjectWindow.dismiss();
    }
    else if (view == saveProjectWindow.saveProjectValidateButton)
    {
      try
      {
        saveProjectWindow.dismiss();
        saveProject(fractal, saveProjectWindow.saveProjectEditText.getText().toString());
        if (saveBeforeQuit)
        {
          saveBeforeQuit = false;
        }
        else
        {
          this.finish();
        }
      }
      catch (IOException e)
      {
        e.printStackTrace();
        (this).runOnUiThread(new Runnable()
        {
          public void run()
          {
            Toast.makeText(getApplicationContext(), getApplicationContext().getString(R.string.Toast_save_project_failed), Toast.LENGTH_SHORT).show();
          }
        });
      }
    }
    else if (view == askSaveProjectWindow.askSaveProjectSaveButton)
    {
      if (project != null)
      {
        if (project.name != null)
        {
          try
          {
            saveProject(fractal, project.name);
          }
          catch (IOException e)
          {
            e.printStackTrace();
          }
          saveBeforeQuit = false;
          this.finish();
        }
      }
      else
      {
        saveBeforeQuit = false;
        askSaveProjectWindow.dismiss();
        saveProjectWindow.show();
      }
    }
    else if (view == askSaveProjectWindow.askSaveProjectCancelButton)
    {
      askSaveProjectWindow.dismiss();
    }
    else if (view == askSaveProjectWindow.askSaveProjectDontSaveButton)
    {
      this.finish();
    }
  }

}